/*
 *  Copyright (C) 1998-2020 by Northwoods Software Corporation. All Rights Reserved.
 */

import * as go from 'gojs';
import { ReactDiagram } from 'gojs-react';
import * as React from 'react';

import './Diagram.scss';
// import SaveIcon from '../../assets/save.svg';
import PublishIcon from '../../assets/publish.svg';
import RevertIcon from '../../assets/revert.svg';
import DiasmallIcon from '../../assets/diasmall.svg';
import DiabigIcon from '../../assets/diabig.svg';
// 组件图标icon
import { getStore } from 'store/store';
import { getFlowGlobalSet, postFlow } from 'admin/servers/servers';
import { message, Spin, Modal, Button, Tooltip, Popconfirm } from 'antd';

import { makeDiagramModal } from './Diagram';
import { CloudSyncOutlined } from '@ant-design/icons';
import { cloneDeep } from 'lodash';

interface DiagramProps {
  nodeDataArray: Array<go.ObjectData>;
  linkDataArray: Array<go.ObjectData>;
  modelData: go.ObjectData;
  skipsDiagramUpdate: boolean;
  /**全屏标识 */
  fixedSpan: any;
  /**版本标识 */
  versionId: number;
  // 处理双击事件
  onDiagramDbEvent: (e: go.DiagramEvent) => void;
  // 处理单击事件
  ononeDiagramDbEvent: (e: go.DiagramEvent) => void;
  /**
   * 处理所有相关的DiagramEvents，在本例中仅更改选择。
   * 在ChangedSelection上，找到相应的数据并设置selectedData状态。
   * @param e a GoJS DiagramEvent
   */
  onDiagramEvent: (e: go.DiagramEvent) => void;
  /**
   * @param obj a JSON-formatted string
   * 当拖拽组件进入图表时, 触发change事件, 将新的数据setState到数据模型中
   */
  onModelChange: (obj: go.IncrementalData) => void;
  /**
   * 处理组件高亮status
   * @param data 被修改的节点
   * @param path 被修改的属性名路径
   * @param value 新的value值
   */
  onStatusChange: (data: any, path: string, value: any) => void;
  /**
   * 处理节点修改
   * @param data 被修改的节点
   * @param value 新的value值
   */
  onNodeChange: (data: any, value: any) => void;
  /**
   * 同步已发布版本到草稿
   */
  onSyncPublishToDraftFlow: () => void;
  /**
   * 切换流程图【已发布】【草稿】标签卡
   */
  onChangeFlowView: (type: 'publish' | 'draft') => void;
}

interface State {
  /**保存中... */
  saving: boolean;
  /**配置流程不正确弹框 */
  validateErrorModal: boolean;
  /**必填字段未填写组件名称 */
  validateRequired: string[];
  /**未被连接到流程的组件名称 */
  validateLink: string[];
  /**分支未被连接的分支名称 */
  validateSubLink: string[];
  /**使用了全局设置，但是没有配置全局 */
  validateGlobalSetNull: string[];
}

let timer: null | NodeJS.Timeout = null;

/**
 * 流程节点集中展示区
 * 示例： AI管理-机器人管理-版本管理-配置话术流程-流程管理
 */
export class DiagramWrapper extends React.PureComponent<DiagramProps, State> {
  /**
   * Ref to keep a reference to the Diagram component, which provides access to the GoJS diagram via getDiagram().
   */
  private diagramRef: React.RefObject<ReactDiagram>;

  /** @internal */
  constructor(props: DiagramProps) {
    super(props);
    this.diagramRef = React.createRef();
    this.state = {
      // myDiagram: null,
      saving: false,
      validateErrorModal: false,
      validateRequired: [],
      validateLink: [],
      validateSubLink: [],
      validateGlobalSetNull: [],
    };
  }
  fullIcon = DiasmallIcon;
  /**
   * Get the diagram reference and add any desired diagram listeners.
   * Typically the same function will be used for each listener, with the function using a switch statement to handle the events.
   */
  public componentDidMount() {
    // console.log('DiagramWrapper -> componentDidMount***');
    if (!this.diagramRef.current) return;
    const diagram = this.diagramRef.current.getDiagram();
    if (diagram instanceof go.Diagram) {
      // 监控选中事件
      diagram.addDiagramListener('ChangedSelection', this.props.onDiagramEvent);
      // 监控双击事件
      diagram.addDiagramListener('ObjectDoubleClicked', this.props.onDiagramDbEvent);
      // 监控单击事件
      diagram.addDiagramListener('ObjectSingleClicked', this.props.ononeDiagramDbEvent);
      // 去掉高亮
      // this.clearHighlightNode(diagram);
    }
    this.autoSave(5000);
  }

  componentDidUpdate() {
    // console.log('流程图容器 DiagramWrapper ->componentDidUpdate!!!!!!!!!!!!');
  }

  /**
   * Get the diagram reference and remove listeners that were added during mounting.
   */
  public componentWillUnmount() {
    if (!this.diagramRef.current) return;
    const diagram = this.diagramRef.current.getDiagram();
    if (diagram instanceof go.Diagram) {
      this.clearHighlightNode(diagram);
      diagram.removeDiagramListener('ChangedSelection', this.props.onDiagramEvent);
      diagram.removeDiagramListener('ObjectDoubleClicked', this.props.onDiagramDbEvent);
      diagram.removeDiagramListener('ObjectSingleClicked', this.props.ononeDiagramDbEvent);
    }
    /**清除定时器 */
    timer && clearInterval(timer);
  }

  /**
   * 将某一个节点展示在视线内
   * @param myDiagram
   * @param hotKey
   */
  private srollToNode(myDiagram: go.Diagram, hotKey: number) {
    const node = myDiagram.findNodeForKey(hotKey);
    if (myDiagram && node) {
      myDiagram.commandHandler.scrollToPart(node);
    }
  }

  /**
   * @param status 节点状态：0 正常，1 高亮，2 失败
   */
  private highlightNodeCopy(
    myDiagram: go.Diagram,
    lightKeyArr: number[],
    linkFromPortMap: any,
    status: number
  ) {
    const nodeDataCopy = cloneDeep(this.props.nodeDataArray);
    nodeDataCopy.forEach((item: any) => {
      const itemCopy = cloneDeep(item);
      if (lightKeyArr.includes(itemCopy.key)) {
        itemCopy.status = status;
      } else {
        itemCopy.status = 0;
      }
      if (itemCopy.category === 'ConditionComponent') {
        // 条件组件
        const linkFromPortArr = linkFromPortMap.get(itemCopy['key']);
        const conditionArray = itemCopy['conditionArray'];
        conditionArray.forEach((data: any) => {
          if (!linkFromPortArr || !linkFromPortArr.includes(data.describe)) {
            data.status = status;
          } else {
            data.status = 0;
          }
        });
      } else if (itemCopy.category === 'IntentComponent') {
        // 意图组件
        const linkFromPortArr = linkFromPortMap.get(itemCopy['key']);
        const intentionArray = itemCopy['intentionArray'];
        intentionArray.forEach((data: any) => {
          if (!linkFromPortArr || !linkFromPortArr.includes(data.text)) {
            data.status = status;
          } else {
            data.status = 0;
          }
        });
      }
      if (itemCopy.category === 'TransferManualServiceComponent') {
        // 转人工组件
        const linkFromPortArr = linkFromPortMap.get(itemCopy['key']);
        let itemKeyArray = [];
        itemKeyArray = itemCopy['transferManualIntentionArray'];
        itemKeyArray.forEach((data: any) => {
          if (!linkFromPortArr || !linkFromPortArr.includes(data.intent)) {
            data.status = status;
          } else {
            data.status = 0;
          }
        });
      } else if (itemCopy.category === 'KeyComponent') {
        // 按键组件
        const linkFromPortArr = linkFromPortMap.get(itemCopy['key']);
        let itemKeyArray = [];
        if (itemCopy.keyFlag === 'PressKey') {
          // 按键类型
          itemKeyArray = itemCopy['pressKeyArray'];
          itemKeyArray.forEach((data: any) => {
            if (!linkFromPortArr || !linkFromPortArr.includes(data.key)) {
              data.status = status;
            } else {
              data.status = 0;
            }
          });
        } else if (itemCopy.keyFlag === 'ReceiveNumber') {
          // 收号类型
          itemKeyArray = itemCopy['receiveNumberDefaultArray'];
          itemKeyArray.forEach((data: any) => {
            if (!linkFromPortArr || !linkFromPortArr.includes(data.intent)) {
              data.status = status;
            } else {
              data.status = 0;
            }
          });
        }
      }
      this.props.onNodeChange(item, itemCopy);
    });
  }

  // 取消节点高亮显示-用于重置高亮
  private clearHighlightNode(myDiagram: go.Diagram) {
    myDiagram.clearSelection();
    // 通过key找到数据模型
    // let data = myDiagram.model.nodeDataArray;
    const data = cloneDeep(this.props.nodeDataArray);
    if (data) {
      data.forEach(item => {
        item.status = 0;
        // myDiagram.model.updateTargetBindings(item); // 更新数据模型到图表
        if (item.category === 'ConditionComponent') {
          // 条件组件
          item.conditionArray.forEach((condition: any) => {
            condition.status = 0;
          });
        } else if (item.category === 'IntentComponent') {
          // 意图组件
          item.intentionArray.forEach((intention: any) => {
            intention.status = 0;
          });
        } else if (item.category === 'KeyComponent') {
          // 按键组件：2种显示类型
          if (item.keyFlag === 'PressKey') {
            // 按键类型
            item.pressKeyArray.forEach((data: any) => {
              data.status = 0;
            });
          } else if (item.keyFlag === 'ReceiveNumber') {
            // 收号类型
            item.receiveNumberDefaultArray.forEach((data: any) => {
              data.status = 0;
            });
          }
        } else if (item.category === 'TransferManualServiceComponent') {
          // 转人工组件
          item.transferManualIntentionArray.forEach((data: any) => {
            data.status = 0;
          });
        }
      });

      this.props.onModelChange({ modifiedNodeData: data });
    }
  }

  /**获取所有组件的key(去重) */
  private getAllNodeKeyArr(nodeDataArray: any) {
    const allNodeKeyArr: any[] = [];
    nodeDataArray.forEach((item: any) => {
      allNodeKeyArr.push(item.key);
    });
    return Array.from(new Set(allNodeKeyArr));
  }

  /**获取所有连线的组件的key(去重) */
  private getLinkArr(linkDataArray: any) {
    const linkArr: any[] = [];
    linkDataArray.forEach((item: any) => {
      linkArr.push(item.from, item.to);
    });
    return Array.from(new Set(linkArr));
  }

  /**获取所有连线的from组件的key(去重) */
  private getLinkFromArr(linkDataArray: any) {
    const linkFromArr: any[] = [];
    linkDataArray.forEach((item: any) => {
      linkFromArr.push(item.from);
    });
    return Array.from(new Set(linkFromArr));
  }

  /**获取所有连线的to组件的key(去重) */
  private getLinkToArr(linkDataArray: any) {
    const getLinkToArr: any[] = [];
    linkDataArray.forEach((item: any) => {
      getLinkToArr.push(item.to);
    });
    return Array.from(new Set(getLinkToArr));
  }

  /**获取所有连线的fromPort */
  private getLinkFromPortMap(linkDataArray: any) {
    const getLinkFromPortMap = new Map();
    linkDataArray.forEach((item: any) => {
      let list = [];
      if (getLinkFromPortMap.has(item.from)) {
        list = getLinkFromPortMap.get(item.from);
      }
      list.push(item.fromPort);
      getLinkFromPortMap.set(item.from, Array.from(new Set(list)));
    });

    return getLinkFromPortMap;
  }

  // 按键组件
  private validateKey(obj: any) {
    // 校验短信模板:true-不通过
    let smsTemplateEmpty = false;
    if (obj.smsSwitch === 'on' && !obj.smsTemplateId) {
      // 短信开启状态,需验证模板id非空
      smsTemplateEmpty = true;
      console.log(
        obj.key,
        obj.category,
        '短信开启-模板id-是否为空：',
        smsTemplateEmpty,
        obj.category
      );
    }

    let timeoutOK = false;
    if (obj.timeout >= 0) {
      timeoutOK = true;
    }

    if (!obj.title || !obj.script || !timeoutOK || smsTemplateEmpty) {
      return obj.key;
    }
    return false;
  }

  // 条件组件
  private validateCondition(obj: any) {
    if (!obj.title || !obj.conditionArray.length) {
      return obj.key;
    }
    return false;
  }

  // 接口组件
  private validateInterface(obj: any) {
    if (!obj.title || !obj.interfaceInfo || !obj.interfaceInfo.interfaceId) {
      return obj.key;
    }
    return false;
  }

  // 跳转组件
  private validateSkip(obj: any) {
    let timeoutOK = false;
    if (obj.timeout >= 0) {
      timeoutOK = true;
    }
    if (!obj.title || !obj.script || !timeoutOK) {
      return obj.key;
    }
    return false;
  }

  // 转人工组件
  private validateTransferManualService(obj: any) {
    // 校验等待音设置:true-不通过
    let voiceEmpty = false;
    if (
      obj.waitVoiceSwitch === 'on' &&
      (!obj.waitVoiceFile ||
        !obj.waitVoiceFileRemote ||
        !obj.tipsVoiceFileRemote ||
        !obj.tipsVoiceFile ||
        !(obj.playDuration >= 0))
    ) {
      // 开启状态,需验证必填项非空
      voiceEmpty = true;
      console.log('key:', obj.key, obj.category, '等待音设置-必填项文件-为空：', voiceEmpty);
    }
    // 校验转给指定号码:true-不通过
    let numberEmpty = false;
    const phone = obj.phoneNumber;
    if (obj.transferManualMode === 1 && !phone) {
      numberEmpty = true;
      console.log(
        'key:',
        obj.key,
        obj.category,
        '校验转给指定号码-必填项：号码-为空>>',
        numberEmpty,
        phone
      );
    }
    if (!obj.title || voiceEmpty || numberEmpty) {
      return obj.key;
    }
    return false;
  }

  // 信息组件
  private validateInfoCollection(obj: any) {
    // 校验短信模板:true-不通过
    let smsTemplateEmpty = false;
    if (obj.smsSwitch === 'on' && !obj.smsTemplateId) {
      // 短信开启状态,需验证模板id非空
      smsTemplateEmpty = true;
      console.log('key:', obj.key, obj.category, '短信开启-模板id-是否为空：', smsTemplateEmpty);
    }

    // 校验超时话术数组
    let timeoutScriptEmpty = [];
    if (obj.timeoutScriptArray.length) {
      timeoutScriptEmpty = obj.timeoutScriptArray.filter((item: any) => {
        // 返回条件-话术非空
        return !item.script;
      });
      console.log('key:', obj.key, obj.category, '超时设置-存储话术为空数组：', timeoutScriptEmpty);
    }
    if (
      !obj.title ||
      !obj.script ||
      !obj.variableName ||
      !obj.intention.length ||
      !obj.timeoutFollowUpAct ||
      timeoutScriptEmpty.length ||
      smsTemplateEmpty
    ) {
      return obj.key;
    }
    return false;
  }

  // 结束组件
  private validateEnd(obj: any) {
    // 校验短信模板:true-不通过
    let smsTemplateEmpty = false;
    if (obj.smsSwitch === 'on' && !obj.smsTemplateId) {
      // 短信开启状态,需验证模板id非空
      smsTemplateEmpty = true;
      console.log('key:', obj.key, obj.category, '短信开启-模板id-是否为空：', smsTemplateEmpty);
    }
    if (!obj.title || !obj.script || smsTemplateEmpty) {
      return obj.key;
    }
    return false;
  }

  /**校验意图组件:校验失败则返回组件的key */
  private validateIntent(obj: any) {
    // 校验短信模板:true-不通过
    let smsTemplateEmpty = false;
    if (obj.smsSwitch === 'on' && !obj.smsTemplateId) {
      // 短信开启状态,需验证模板id非空
      smsTemplateEmpty = true;
      console.log('key:', obj.key, obj.category, '短信开启-模板id-是否为空：', smsTemplateEmpty);
    }

    if (obj.useGlobalNotUnderstoodSwitch === 'on' || obj.useGlobalTimeoutSwitch === 'on') {
      // 1.开启全局设置（未理解，超时）
      // 必须检测是否有全局设置
      // 组件不提示
      console.log('key:', obj.key, obj.category, '>>>>>>>>>组件使用全局设置');
    }
    // 2.未理解-自定义设置
    if (obj.useGlobalNotUnderstoodSwitch !== 'on') {
      // 校验未理解话术设置数组
      let notUnderstoodScriptEmpty = [];
      if (obj.notUnderstoodScriptArray.length) {
        // 数据长度非0
        notUnderstoodScriptEmpty = obj.notUnderstoodScriptArray.filter((item: string) => {
          // 返回条件-话术为空
          return !item;
          // return item === "" || item === undefined || item === null;
        });
        console.log(
          'key:',
          obj.key,
          obj.category,
          '未理解设置-存储话术为空数组：',
          notUnderstoodScriptEmpty
        );
      }
      // undefined为未理解跳转节点有添加，但是值为空，不允许
      if (notUnderstoodScriptEmpty.length || !obj.notUnderstoodFollowUpAct) {
        return obj.key;
      }
    }

    // 3.超时-自定义设置
    if (obj.useGlobalTimeoutSwitch !== 'on') {
      // 校验超时话术数组
      let timeoutScriptEmpty = [];
      if (obj.timeoutScriptArray.length) {
        timeoutScriptEmpty = obj.timeoutScriptArray.filter((item: any) => {
          // 返回条件-话术为空
          return !item.script;
        });
        console.log(
          'key:',
          obj.key,
          obj.category,
          '超时设置-存储话术为空数组：',
          timeoutScriptEmpty
        );
      }
      // undefined为超时跳转节点有添加，但是值为空，不允许
      if (!obj.timeoutFollowUpAct || timeoutScriptEmpty.length) {
        return obj.key;
      }
    }

    // 校验
    if (!obj.title || !obj.script || !obj.intentionArray.length || smsTemplateEmpty) {
      return obj.key;
    }
    return false;
  }

  /**校验所有按键 */
  private validateAllItemKey(nodeDataArray: any[]) {
    const invalidItemKeyArr: any[] = [];
    const linkFromPortMap = this.getLinkFromPortMap(this.props.linkDataArray);
    const validateSubLink = this.state.validateSubLink;

    nodeDataArray.forEach((item, index) => {
      if (item.category === 'KeyComponent') {
        // 按键组件：2种显示类型
        const linkFromPortArr = linkFromPortMap.get(item['key']);
        let itemKeyArray = [];
        if (item.keyFlag === 'PressKey') {
          // 按键类型
          itemKeyArray = item['pressKeyArray'];
          itemKeyArray.forEach((data: any) => {
            if (!linkFromPortArr || !linkFromPortArr.includes(data.key)) {
              invalidItemKeyArr.push({ key: data.key, index });
              validateSubLink.push(data.key);
            }
          });
        } else if (item.keyFlag === 'ReceiveNumber') {
          // 收号类型
          itemKeyArray = item['receiveNumberDefaultArray'];
          itemKeyArray.forEach((data: any) => {
            if (!linkFromPortArr || !linkFromPortArr.includes(data.intent)) {
              invalidItemKeyArr.push({ intent: data.intent, index });
              validateSubLink.push(data.intent);
            }
          });
        }
      }
      if (item.category === 'TransferManualServiceComponent') {
        // 转人工组件
        const linkFromPortArr = linkFromPortMap.get(item['key']);
        let itemKeyArray = [];
        itemKeyArray = item['transferManualIntentionArray'];
        itemKeyArray.forEach((data: any) => {
          if (!linkFromPortArr || !linkFromPortArr.includes(data.intent)) {
            invalidItemKeyArr.push({ intent: data.intent, index });
            validateSubLink.push(data.intent);
          }
        });
      }
    });
    this.setState({
      validateSubLink: Array.from(new Set(validateSubLink)),
    });
    // 去重后返回
    return Array.from(new Set(invalidItemKeyArr));
  }

  /**校验所有条件 */
  private validateAllCondition(nodeDataArray: any[]) {
    const conditionArrKey: any[] = [];
    const linkFromPortMap = this.getLinkFromPortMap(this.props.linkDataArray);
    const validateSubLink = this.state.validateSubLink;

    nodeDataArray.forEach((item, index) => {
      if (item.category === 'ConditionComponent') {
        // 条件组件
        const linkFromPortArr = linkFromPortMap.get(item['key']);
        const conditionArray = item['conditionArray'];

        conditionArray.forEach((data: any) => {
          if (!linkFromPortArr || !linkFromPortArr.includes(data.describe)) {
            conditionArrKey.push({ describe: data.describe, index });
            validateSubLink.push(data.describe);
          }
        });
      }
    });
    this.setState({
      validateSubLink: Array.from(new Set(validateSubLink)),
    });
    // 去重后返回
    return Array.from(new Set(conditionArrKey));
  }

  /**校验所有意图 */
  private validateAllIntention(nodeDataArray: any[]) {
    const invalidIntentionArr: any[] = [];
    const linkFromPortMap = this.getLinkFromPortMap(this.props.linkDataArray);
    const validateSubLink = this.state.validateSubLink;

    nodeDataArray.forEach((item, index) => {
      if (item.category === 'IntentComponent') {
        // 意图组件
        const linkFromPortArr = linkFromPortMap.get(item['key']);
        const intentionArray = item['intentionArray'];

        intentionArray.forEach((data: any) => {
          if (!linkFromPortArr || !linkFromPortArr.includes(data.text)) {
            invalidIntentionArr.push({ text: data.text, index });
            validateSubLink.push(data.text);
          }
        });
      }
    });
    this.setState({
      validateSubLink: Array.from(new Set(validateSubLink)),
    });
    // 去重后返回
    return Array.from(new Set(invalidIntentionArr));
  }
  /**检查是否有全局配置数据 */
  private async checkFlowGlobalSet(versionId: number) {
    try {
      const { data } = await getFlowGlobalSet({ versionId });
      if (data) {
        // 有全局配置
        return true;
      }
    } catch (error) {
      console.error(`系统异常: ${JSON.stringify(error)}`);
    }
    // 无全局配置
    return false;
  }

  /**校验所有组件: 返回不合规的组件key的数组(去重) */
  private validateAll(nodeDataArray: any[], hasFlowGlobalSet: boolean) {
    const arrKey: number[] = [];
    const allKey = this.getAllNodeKeyArr(nodeDataArray);
    /**
     * 统一连线，不分方向 from to
     */
    const linkArr = this.getLinkArr(this.props.linkDataArray);

    const linkFromArr = this.getLinkFromArr(this.props.linkDataArray);
    const linkToArr = this.getLinkToArr(this.props.linkDataArray);

    const { validateLink, validateRequired, validateGlobalSetNull } = this.state;

    nodeDataArray.forEach(item => {
      // 收集: 没有连线的组件的key
      if (!linkArr.includes(item.key)) {
        arrKey.push(item.key);
        validateLink.push(item.title);
        return;
      }
      if (item.category === 'StartComponent') {
        // 开始组件--只能在 linkFromArr 中
        if (!linkFromArr.includes(item.key) || linkToArr.includes(item.key)) {
          arrKey.push(item.key);
          validateLink.push(item.title);
          return;
        }
      } else if (item.category === 'EndComponent') {
        // 结束组件--只能在 linkToArr 中
        if (linkFromArr.includes(item.key) || !linkToArr.includes(item.key)) {
          arrKey.push(item.key);
          validateLink.push(item.title);
          return;
        }
      } else {
        // 其余组件--必须同时在 linkFromArr 和 linkToArr 中
        if (!linkFromArr.includes(item.key) || !linkToArr.includes(item.key)) {
          arrKey.push(item.key);
          validateLink.push(item.title);
          return;
        }
      }

      // 前面校验通过，进行字段规则校验

      // 收集: 组件字段不合规的key
      switch (item.category) {
        case 'IntentComponent':
          // 意图组件
          const validate_intent = this.validateIntent(item);
          if (typeof validate_intent === 'number') {
            // 必填字段-验证为空
            arrKey.push(validate_intent);
            validateRequired.push(item.title);
          } else {
            // 意图组件必填字段-全部非空
            // 进一步验证，输入字段是否合法、存在
            if (
              item.useGlobalTimeoutSwitch === 'on' ||
              item.useGlobalNotUnderstoodSwitch === 'on'
            ) {
              if (!hasFlowGlobalSet) {
                // 检查是否有全局配置数据，如果没有数据，需要进行提示
                console.log('出现全局设置为空❌❌❌❌❌❌');
                arrKey.push(item.key); // 高亮当前节点
                validateGlobalSetNull.push(item.title);
              }
            }
            // 非全局设置时，校验超时-跳转节点，是否真实存在
            if (
              item.useGlobalTimeoutSwitch !== 'on' &&
              !allKey.includes(item.timeoutFollowUpAct.key)
            ) {
              arrKey.push(item.key);
              validateRequired.push(item.title);
              // todo-清除跳转节点的值-失效
              setTimeout(() => {
                const itemCopy = cloneDeep(item);
                itemCopy.timeoutFollowUpAct = undefined;
                itemCopy.status = 2;
                this.props.onNodeChange(item, itemCopy);
              }, 0);
            }
            // 非全局设置时，校验未理解-跳转节点，是否真实存在
            if (
              item.useGlobalNotUnderstoodSwitch !== 'on' &&
              !allKey.includes(item.notUnderstoodFollowUpAct.key)
            ) {
              arrKey.push(item.key);
              validateRequired.push(item.title);
              // todo-清除跳转节点的值-失效
              setTimeout(() => {
                const itemCopy = cloneDeep(item);
                itemCopy.notUnderstoodFollowUpAct = undefined;
                itemCopy.status = 2;
                this.props.onNodeChange(item, itemCopy);
              }, 0);
            }
          }
          break;
        case 'KeyComponent':
          const validate_key = this.validateKey(item);
          if (typeof validate_key === 'number') {
            arrKey.push(validate_key);
            validateRequired.push(item.title);
          }
          break;
        case 'ConditionComponent':
          const validate_condition = this.validateCondition(item);
          if (typeof validate_condition === 'number') {
            arrKey.push(validate_condition);
            validateRequired.push(item.title);
          }
          break;
        case 'InterfaceComponent':
          const validate_interface = this.validateInterface(item);
          if (typeof validate_interface === 'number') {
            arrKey.push(validate_interface);
            validateRequired.push(item.title);
          }
          break;
        case 'TransferManualServiceComponent':
          const validate_manual = this.validateTransferManualService(item);
          if (typeof validate_manual === 'number') {
            arrKey.push(validate_manual);
            validateRequired.push(item.title);
          }
          break;
        case 'InfoCollectionComponent':
          // 信息收集组件
          const validate_collect = this.validateInfoCollection(item);
          if (typeof validate_collect === 'number') {
            arrKey.push(validate_collect);
            validateRequired.push(item.title);
          } else {
            // 必填字段-全部非空
            // 校验超时-跳转节点，是否真实存在
            if (!allKey.includes(item.timeoutFollowUpAct.key)) {
              arrKey.push(item.key);
              validateRequired.push(item.title);
              // todo-清除跳转节点的值-失效
              setTimeout(() => {
                const itemCopy = cloneDeep(item);
                itemCopy.timeoutFollowUpAct = undefined;
                itemCopy.status = 2;
                this.props.onNodeChange(item, itemCopy);
              }, 0);
            }
          }
          break;
        case 'SkipComponent':
          const validate_skip = this.validateSkip(item);
          if (typeof validate_skip === 'number') {
            arrKey.push(validate_skip);
            validateRequired.push(item.title);
          }
          break;
        case 'EndComponent':
          const validate_end = this.validateEnd(item);
          if (typeof validate_end === 'number') {
            arrKey.push(validate_end);
            validateRequired.push(item.title);
          }
          break;
      }
    });
    // 去重后返回
    this.setState({
      validateLink: Array.from(new Set(validateLink)),
      validateRequired: Array.from(new Set(validateRequired)),
      validateGlobalSetNull: Array.from(new Set(validateGlobalSetNull)),
    });
    return Array.from(new Set(arrKey));
  }

  /**
   * 按钮-保存操作
   * 1. 先校验
   * 2. 校验不通过->高亮提示
   * 3. 校验通过->保存
   * 4. 清除对应流程缓存
   */
  private async publishFlow() {
    console.log('DiagramWrapper ->publishFlow-> 流程保存并发布-点击');
    const { versionId, nodeDataArray, linkDataArray } = this.props;
    // 查询全局配置
    const hasFlowGlobalSet = await this.checkFlowGlobalSet(versionId);

    // 获取图表配置
    const myDiagram = this.diagramRef.current && this.diagramRef.current.getDiagram();

    const lightKeyArr = this.validateAll(nodeDataArray, hasFlowGlobalSet);
    const lightConditionArr = this.validateAllCondition(nodeDataArray);
    const lightIntentionArr = this.validateAllIntention(nodeDataArray);
    const lightKeyItemArr = this.validateAllItemKey(nodeDataArray);
    const linkFromPortMap = this.getLinkFromPortMap(linkDataArray);

    if (
      lightKeyArr.length ||
      lightConditionArr.length ||
      lightIntentionArr.length ||
      lightKeyItemArr.length
    ) {
      // 校验失败
      if (myDiagram) {
        console.log('>>>>>>>>>>>>>>>>>>>>>>>>>>>>校验失败 ❌*****nodeDataArray-节点高亮');
        // 高亮验证不通过组件
        this.highlightNodeCopy(myDiagram, lightKeyArr, linkFromPortMap, 2);
      }
      this.setState({
        validateErrorModal: true,
      });
      return;
    }
    // 校验成功
    console.log('>>>>>>>>>>>>>>>>>>>>>>>>>>>>校验成功 ✅*****nodeDataArray-节点去除高亮');
    if (myDiagram) {
      // 清除高亮组件
      this.clearHighlightNode(myDiagram);
      // 默认只高亮开始组件（？？？都不高亮）
      this.highlightNodeCopy(myDiagram, [], new Map(), 0);
    }
    this.setState({
      saving: true,
    });
    // 校验通过，开始保存
    // 注意：前面校验过程中，有变更nodeDataArray，linkDataArray，下面请求需要用最新的

    await this.saveFlowData(true);
    this.setState({
      saving: false,
    });
  }

  /**保存【草稿】 */
  private saveFlowDraft() {
    this.saveFlowData(false);
  }

  /**
   * 请求保存流程接口
   * @param isPublish 是否发布
   */
  private async saveFlowData(isPublish: boolean) {
    const { versionId, nodeDataArray, linkDataArray } = this.props;
    if (!versionId) return;
    try {
      // 当前流程数据
      const currentFlow = {
        class: 'GraphLinksModel',
        linkFromPortIdProperty: 'fromPort',
        linkToPortIdProperty: 'toPort',
        nodeDataArray: nodeDataArray,
        linkDataArray: linkDataArray,
      };
      // console.log(
      //   new Date(),
      //   '**********************保存流程-请求=====是否【发布】>',
      //   isPublish,
      //   currentFlow
      // );
      // 提交到数据库-草稿
      const res = await postFlow({
        versionId: versionId,
        content: JSON.stringify(currentFlow),
        isPublish: isPublish,
      });
      const { code } = res;
      if (code === 30021) {
        // 用户掉线（如果因网络掉线等原因），其他人正在编辑
        // 网络恢复后，临时保存，弹窗提示切换已发布
        /**清除定时器 */
        timer && clearInterval(timer);
        Modal.info({
          title: '提醒',
          content: '草稿正在被其他用户使用，请稍后尝试',
          okText: '返回已发布版本',
          closable: false,
          onOk: () => {
            // 切换【已发布版本】
            this.props.onChangeFlowView('publish');
          },
        });
        return;
      }
      isPublish && message.success('流程发布成功');
    } catch (error) {
      console.error(`系统异常: ${JSON.stringify(error)}`);
    }
  }

  /**
   * 自动保存【草稿】
   * @param time 间隔时间-毫秒
   */
  private autoSave(time: number) {
    console.log('流程图-间隔5s-更新-开启');
    timer = setInterval(() => {
      this.saveFlowData(false);
    }, time);
  }

  /**
   * 撤销操作
   */
  private revertFlow() {
    console.log('撤销操作');
    const myDiagram = this.diagramRef.current?.getDiagram();
    if ('undefined' === typeof myDiagram || !myDiagram) {
      console.log('浏览器不兼容此功能，请使用高版本谷歌浏览器！');
      return false;
    }
    myDiagram.undoManager.undo();
  }

  // 全屏操作
  private fullFlow() {
    this.props.fixedSpan('full'); //DiabigIcon  //DiasmallIcon
    const store = getStore();
    if (store.get('isFullScreen') === true) {
      store.set('isFullScreen', false);
    } else {
      store.set('isFullScreen', true);
    }
    if (this.fullIcon === DiasmallIcon) {
      this.fullIcon = DiabigIcon;
    } else {
      this.fullIcon = DiasmallIcon;
    }
  }

  /**
   * 同步已发布版本到草稿
   */
  private syncPublishToDraftFlow() {
    // 调用父组件方法，将已发布流程替换到草稿流程，进行同步
    this.props.onSyncPublishToDraftFlow();
  }

  // 初始化图表
  private initDiagramModel = () => {
    const myDiagram = makeDiagramModal();
    // todo-更新加星号，保存后去掉星号
    // myDiagram.addDiagramListener('Modified', e => {
    //   console.log('图表init后监听-流程图更新', e);
    //   const idx = document.title.indexOf('*');
    //   if (myDiagram.isModified) {
    //     if (idx < 0) document.title += '*';
    //   } else {
    //     if (idx >= 0) document.title = document.title.substr(0, idx);
    //   }
    // });
    return myDiagram;
  };

  public render() {
    return (
      <div className="diagram_container">
        <Spin spinning={this.state.saving}>
          <ReactDiagram
            ref={this.diagramRef}
            divClassName="diagram-component"
            initDiagram={this.initDiagramModel}
            nodeDataArray={this.props.nodeDataArray}
            linkDataArray={this.props.linkDataArray}
            modelData={this.props.modelData}
            onModelChange={this.props.onModelChange}
            skipsDiagramUpdate={this.props.skipsDiagramUpdate}
          />
          <Tooltip placement="left" title="发布">
            <div className="btn_container" onClick={this.publishFlow.bind(this)}>
              <img className="btn_save" src={PublishIcon} alt="发布" width={22} />
            </div>
          </Tooltip>
          <Tooltip placement="left" title="撤销">
            <div className="btn_container revert_container" onClick={this.revertFlow.bind(this)}>
              <img className="btn_revert" src={RevertIcon} alt="撤销" width={22} />
            </div>
          </Tooltip>
          <Tooltip placement="left" title="全屏">
            <div className="btn_container full_container" onClick={this.fullFlow.bind(this)}>
              <img className="btn_full" src={this.fullIcon} alt="全屏" width={20} />
            </div>
          </Tooltip>
          <Popconfirm
            placement="bottomRight"
            title="同步已发布版本到草稿？"
            onConfirm={this.syncPublishToDraftFlow.bind(this)}
          >
            <Tooltip placement="left" title="同步已发布版本到草稿">
              <div className="btn_container sync_publish_container">
                <CloudSyncOutlined style={{ fontSize: 22, color: '#fff' }} />
              </div>
            </Tooltip>
          </Popconfirm>
          {/* <Tooltip placement="left" title="暂存草稿">
            <div className="temp_btn_container" onClick={this.autoSave.bind(this)}>
              <img className="btn_save" src={SaveIcon} alt="暂存草稿" width={20} />
            </div>
          </Tooltip> */}
        </Spin>
        <Modal
          title="流程配置不正确提示"
          visible={this.state.validateErrorModal}
          onCancel={() => {
            this.setState({
              validateErrorModal: false,
              validateSubLink: [],
              validateRequired: [],
              validateLink: [],
              validateGlobalSetNull: [],
            });
          }}
          footer={[
            <Button
              key="back"
              type="primary"
              onClick={() => {
                this.setState({
                  validateErrorModal: false,
                  validateSubLink: [],
                  validateRequired: [],
                  validateLink: [],
                  validateGlobalSetNull: [],
                });
              }}
            >
              继续修改
            </Button>,
          ]}
        >
          {this.state.validateRequired.length > 0 && (
            <p>
              组件内必填字段未填写：
              {this.state.validateRequired.map(item => (
                <span key={item}>【{item}】</span>
              ))}
            </p>
          )}
          {this.state.validateLink.length > 0 && (
            <p>
              组件未被连接到流程：
              {this.state.validateLink.map(item => (
                <span key={item}>【{item}】</span>
              ))}
            </p>
          )}
          {this.state.validateSubLink.length > 0 && (
            <p>
              组件内分支未被连接流程：
              {this.state.validateSubLink.map(item => (
                <span key={item}>【{item}】</span>
              ))}
            </p>
          )}
          {this.state.validateGlobalSetNull.length > 0 && <p>没有配置全局未理解和超时静默设置</p>}
        </Modal>
      </div>
    );
  }
}
